/*
 *
 * Copyright (c) 2022 The Raptor Authors. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#ifndef RAPTOR_LITE_SRC_WINDOWS_TCP_CLIENT_H_
#define RAPTOR_LITE_SRC_WINDOWS_TCP_CLIENT_H_

#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>

#include "raptor-lite/impl/property.h"
#include "raptor-lite/impl/client.h"

#include "raptor-lite/utils/thread.h"
#include "raptor-lite/utils/sync.h"
#include "raptor-lite/utils/mpscq.h"
#include "raptor-lite/utils/slice_buffer.h"
#include "raptor-lite/utils/status.h"

#include "src/common/resolve_address.h"
#include "src/common/sockaddr.h"
#include "src/common/common.h"
#include "src/windows/iocp_thread.h"

namespace raptor {
struct InternalClientEventNode;

class TcpClient final {
public:
    explicit TcpClient(ClientHandler *handler);
    ~TcpClient();

    raptor_error Init();
    raptor_error Start();
    void Shutdown();
    raptor_error Connect(const std::string &addr);
    bool SendMsg(const Slice &msg);
    void Disconnect();
    bool IsConnected() const { return is_connected_; }
    size_t SendBufferDataSize() const { return snd_buffer_.GetBufferLength(); }

private:
    void IocpProcess(void *);
    void WorkThread(void *);

    void OnErrorEvent(int event_type, int err);
    void OnSendEvent(DWORD bytes);
    void OnRecvEvent(DWORD bytes);
    void OnConnected();

    bool AsyncSend();
    bool AsyncRecv();

    bool RecvImpl(size_t size);
    bool SendImpl(size_t size);

    void TransferData(const Slice &s);
    void Dispatch(InternalClientEventNode *);
    Slice ReadSliceFromRecvBuffer(size_t c);
    void ParsingProtocol();

    raptor_error GetConnectExIfNecessary(SOCKET s);
    raptor_error AsyncConnect(const raptor_resolved_address *addr, int tcp_user_timeout_ms);

private:
    ClientHandler *service_;
    bool shutdown_;
    bool is_connected_;
    bool send_pending_;
    SOCKET fd_;
    LPFN_CONNECTEX connectex_;

    // for recv
    size_t next_package_size_;

    int64_t last_sendtime_;
    int64_t last_recvtime_;
    AtomicInt64 count_;

    char *buffer_;

    OverLappedEx *conn_ole_;
    OverLappedEx *send_ole_;
    OverLappedEx *recv_ole_;

    SliceBuffer rcv_buffer_;
    SliceBuffer snd_buffer_;

    Mutex smtx_; // for snd_buffer_;
    Mutex cmtx_; // for cv
    ConditionVariable cv_;

    Thread mpscq_thd_;
    Thread iocp_thd_;
    Iocp iocp_;
    MultiProducerSingleConsumerQueue mpscq_;
};

} // namespace raptor
#endif // RAPTOR_LITE_SRC_WINDOWS_TCP_CLIENT_H_
